import datetime
import json
import re
from pathlib import Path
from typing import Union

import eyed3
from mutagen.easyid3 import EasyID3
from mutagen.flac import FLAC, Picture
from mutagen.id3 import APIC, ID3, TSRC
from mutagen.mp3 import EasyMP3
from PIL import Image
from pydub import AudioSegment
from pydub.utils import mediainfo

EasyID3.RegisterTextKey("comment", "COMM::eng")
EasyID3.RegisterTextKey("initialkey", "TKEY")
EasyID3.RegisterTextKey("isrc", "TSRC")

tags = {
    "album": "TALB",
    "artist": "TPE1",
    "albumartist": "TPE2",
    "isrc": "TSRC",
    "title": "TIT2",
    "tracknumber": "TRCK",
    "organization": "TPUB",
    "copyright": "TCOP",
    "location": "WXXX:",
    "comment": "COMM::eng",
    "genre": "TCON",
    "bpm": "TBPM",
    "date": "TDRC",
    "initialkey": "TKEY",
    "lyrics": "USLT::xxx",
}

with open("GenreList.json", "r", encoding="utf-8-sig") as file:
    genres = json.load(file)


class Track:
    path: Path
    tracknumber: str
    tracktotal: str
    title: str
    artist: str
    album: str
    albumartist: str
    date: str
    genre: str
    comment: str
    bpm: str
    initialkey: str
    isrc: str
    organization: str
    copyright: str
    location: str
    lyrics: str

    def set(self, attr) -> None:
        ...

    def __str__(self) -> str:
        return (
            f"Track Number: {self.tracknumber} / Track Total: {self.tracktotal}\n"
            f"Title: {self.title}\n"
            f"Artist: {self.artist}\n"
            f"Album: {self.album}\n"
            f"Album Artist: {self.albumartist}\n"
            f"Date: {self.date}\n"
            f"Track Genre: {self.genre}\n"
            f"Comment: {self.comment}\n"
            f"BPM: {self.bpm} / Key: {self.initialkey}\n"
            f"ISRC: {self.isrc} / Publisher: {self.organization} / Copyright: {self.copyright}\n"
            f"URL: {self.location}\n"
        )


class FlacTrack(Track):
    flac: FLAC

    def __init__(self, trackpath) -> None:
        self.path = Path(trackpath)
        self.flac = FLAC(trackpath)
        # print(self.flac)

        self.set("discnumber")
        self.set("disctotal")

        self.set("tracknumber")
        self.set("tracktotal")

        self.set("title")
        self.set("artist")
        self.set("album")
        self.set("albumartist")

        self.set("date")
        self.set("genre")
        self.set("comment")

        self.set("bpm")
        self.set("initialkey")

        self.set("isrc")
        self.set("organization")
        self.set("copyright")
        self.set("location")

        self.set("lyrics")

        self.setCover()

    def set(self, attr: str) -> None:
        """
        Set Attribute for FlacTrack object from FLAC file
        args:
        self.file
        """
        try:
            if len(self.flac[attr]) == 1:
                self.__setattr__(attr, self.flac[attr][0])
            else:
                self.__setattr__(attr, ", ".join(self.flac[attr]))
        except:
            self.__setattr__(attr, None)

    def setTag(self, tag: str, value: str) -> None:
        self.__setattr__(tag, value)
        self.flac[tag] = value
        self.flac.save()

    def setCover(self) -> None:
        try:
            self.cover = self.flac.pictures[0].data
        except:
            self.cover = None

    def saveCover(self) -> None:
        with open(f"{self.path.parent}/cover.jpg", "wb") as file:
            file.write(self.flac.pictures[0].data)

    def getReleaseDate(self) -> datetime.datetime:
        release_datetime = datetime.datetime(2000, 1, 1)
        try:
            day = self.comment.split(" ")[2]
            month = self.comment.split(" ")[3]
            release_datetime = datetime.datetime.strptime(
                f"{day} {month} {self.date}", "%d %B %Y"
            )
        except:
            try:
                release_datetime = datetime.datetime.strptime(f"{self.date}", "%Y")
            except:
                release_datetime = datetime.datetime.now().strftime("%d %B %Y")
        return release_datetime

    def getGenre(self, splitter: str) -> list[str]:
        if self.genre is None:
            genre = [None]
        else:
            genre = []
            for item in self.genre.split(splitter):
                for key in genres:
                    if item == key:
                        genre.append(item.lstrip().rstrip())
                    elif item in genres[key]:
                        genre.append(key)
        return list(dict.fromkeys(genre))

    def getSubgenre(self, splitter: str) -> Union[list, None]:
        if self.genre is None:
            subgenre = None
        else:
            subgenre = []
            for item in self.genre.split(splitter):
                for key in genres:
                    if item in genres[key]:
                        subgenre.append(item.lstrip().rstrip())
        return subgenre

    def getArtist(self) -> Union[list, None]:
        if self.artist is None:
            artist = None
        else:
            artist = [
                item.lstrip().rstrip()
                for item in re.split(",|&|feat.|ft.", self.artist)
            ]
        return artist

    def getLabel(self) -> Union[str, None]:
        if self.copyright is None:
            label = None
        else:
            label = self.copyright
        return label

    def getAlbumArtist(self) -> Union[list, None]:
        if self.albumartist is None:
            albumartist = None
        else:
            albumartist = [
                item.lstrip().rstrip()
                for item in re.split(",|&|feat.|ft.", self.albumartist)
            ]
        return albumartist

    def resizeImage(self, w: int, h: int):
        self.saveCover()
        image = Image.open(f"{self.path.parent}/cover.jpg")
        image.thumbnail((w, h), Image.Resampling.LANCZOS)
        exif = image.getexif()
        exif[305] = "Monstercat Postmaker Ver. 2.0"
        exif[315] = "Blinzy a.k.a L4zzur"
        image.save(f"{self.path.parent}/cover.jpg", "JPEG", quality=100, exif=exif)

        cover = Picture()
        with open(f"{self.path.parent}/cover.jpg", "rb") as file:
            cover.data = file.read()

        cover.mime = "image/jpeg"
        self.flac.clear_pictures()
        self.flac.add_picture(cover)
        self.flac.save()

    def converToMP3(self) -> None:
        self.saveCover()
        tmp = AudioSegment.from_file(self.path, "flac")
        tmp.export(
            f"{self.path.parent}/{self.path.stem}.mp3",
            format="mp3",
            bitrate="320k",
            tags=mediainfo(self.path).get("TAG", {}),
        )
        audio = ID3(f"{self.path.parent}/{self.path.stem}.mp3")
        with open(f"{self.path.parent}/cover.jpg", "rb") as albumart:
            audio["APIC"] = APIC(
                encoding=3,
                mime="image/jpeg",
                type=3,
                data=albumart.read(),
            )
        audio.save()

    def updateInfo(self, info: dict) -> None:
        print(self.flac)
        for tag in info:
            if info[tag] == "< various >":
                pass
            elif info[tag] != "":
                self.__setattr__(tag, info[tag])
                self.flac[tag] = info[tag]
            else:
                self.__setattr__(tag, None)
                self.flac[tag] = ""
        self.flac.save()


class MP3Track(Track):
    mp3: EasyMP3

    def __init__(self, trackpath) -> None:
        self.path = Path(trackpath)
        self.mp3 = EasyMP3(trackpath, ID3=ID3)
        self.id3 = ID3(trackpath)
        self.easyid3 = EasyID3(trackpath)

        with open("result.txt", "w") as fp:
            fp.write(str(self.mp3))

        self.set("tracknumber")
        try:
            self.tracktotal = self.tracknumber.split("/")[1]
        except:
            self.tracktotal = None
        try:
            self.tracknumber = self.tracknumber.split("/")[0]
        except:
            pass

        self.set("title")
        self.set("artist")
        self.set("album")
        self.set("albumartist")

        self.set("date")
        self.set("genre")
        self.set("comment")

        self.set("bpm")
        self.set("initialkey")

        self.set("isrc")
        self.set("organization")
        self.set("copyright")
        try:
            self.location = self.mp3[tags["location"]].url
        except:
            self.location = None

        try:
            self.lyrics = self.mp3[tags["lyrics"]].text
        except:
            self.lyrics = None

        self.setCover()

    def setCover(self) -> None:

        try:
            self.cover = self.mp3["APIC:"].data
        except:
            self.cover = None

    def set(self, attr) -> None:
        try:
            self.__setattr__(attr, str(self.mp3[tags[attr]].text[0]))
        except:
            self.__setattr__(attr, None)

    def updateInfo(self, info: dict) -> None:
        audiofile = eyed3.load(self.path)

        def check(info: dict, tag: str) -> bool:
            if info[tag] != "< various >" and info[tag] != getattr(self, tag):
                return True
            else:
                return False

        if check(info, "album"):
            self.album = audiofile.tag.album = info["album"]
        if check(info, "artist"):
            self.artist = audiofile.tag.artist = info["artist"]
        if check(info, "albumartist"):
            self.albumartist = audiofile.tag.album_artist = info["albumartist"]
        if check(info, "title"):
            self.title = audiofile.tag.title = info["title"]

        if check(info, "tracknumber"):
            self.tracknumber = info["tracknumber"]
        if check(info, "tracktotal"):
            self.tracktotal = info["tracktotal"]
        if (
            info["tracktotal"] != ""
            and info["tracktotal"] != "< various >"
            and info["tracknumber"] != "< various >"
        ):
            audiofile.tag.track_num = (
                info["tracknumber"],
                info["tracktotal"],
            )
        else:
            if info["tracknumber"] != "< various >":
                audiofile.tag.track_num = (int(info["tracknumber"]), None)

        if check(info, "genre"):
            self.genre = audiofile.tag.genre = info["genre"]
        if check(info, "date"):
            self.date = audiofile.tag.recording_date = info["date"]

        if check(info, "comment"):
            self.comment = info["comment"]
            audiofile.tag.comments.set(info["comment"])

        if check(info, "bpm") and info["bpm"] != "":
            self.bpm = info["bpm"]
            audiofile.tag.bpm = int(info["bpm"])

        if check(info, "copyright"):
            self.copyright = audiofile.tag.copyright = info["copyright"]
        if check(info, "organization"):
            self.organization = audiofile.tag.publisher = info["organization"]

        if check(info, "isrc"):
            self.isrc = info["isrc"]
            self.mp3[tags["isrc"]] = TSRC(encoding=3, text=info["isrc"])

        if check(info, "location"):
            self.location = info["location"]
            audiofile.tag.user_url_frames.set(info["location"])

        if check(info, "lyrics"):
            self.lyrics = info["lyrics"]
            audiofile.tag.lyrics.set(info["lyrics"])

        self.mp3.save()
        audiofile.tag.save()


# track = FlacTrack(
#     "E:/YandexDisk/Monstercat/Singles [MCS]/2023/(23.01.18) TOMB - Unbound/TOMB - Unbound.flac"
# )

# track = MP3Track(
#     "E:/YandexDisk/Monstercat/Singles [MCS]/2023/(23.01.27) WILL K & Eddie Thoneick - Carry Me Home/WILL K & Eddie Thoneick - Carry Me Home.mp3"
# )
